package org.elasticsearch;

import com.obs.services.model.AccessControlList;
import com.obs.services.model.StorageClassEnum;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.cluster.metadata.RepositoryMetaData;
import org.elasticsearch.common.blobstore.BlobContainer;
import org.elasticsearch.common.blobstore.BlobPath;
import org.elasticsearch.common.blobstore.BlobStore;
import org.elasticsearch.common.blobstore.BlobStoreException;
import org.elasticsearch.common.unit.ByteSizeValue;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;


/**
 * OBS BlobStore
 */
public class ObsBlobStore implements BlobStore {

    private static final Logger logger = LogManager.getLogger(ObsBlobStore.class);

    private final ObsService service;

    private final String bucket;

    private final ByteSizeValue bufferSize;

    private final boolean serverSideEncryption;

    private final AccessControlList cannedACL;
    private final StorageClassEnum storageClass;

    private final RepositoryMetaData repositoryMetadata;

    private final Stats stats = new Stats();


    ObsBlobStore(ObsService service, String bucket, boolean serverSideEncryption,
                 ByteSizeValue bufferSize, String acl, String storageClass,
                 RepositoryMetaData repositoryMetadata) {
        this.service = service;
        this.bucket = bucket;
        this.serverSideEncryption = serverSideEncryption;
        this.bufferSize = bufferSize;
        this.cannedACL = initAcl(acl);
        this.storageClass = initStorageClass(storageClass);
        this.repositoryMetadata = repositoryMetadata;
    }


    @Override
    public String toString() {
        return bucket;
    }

    public ObsReference clientReference() {
        return service.client(repositoryMetadata);
    }

    int getMaxRetries() {
        return service.settings(repositoryMetadata).maxRetries;
    }

    public String bucket() {
        return bucket;
    }

    public boolean serverSideEncryption() {
        return serverSideEncryption;
    }

    public long bufferSizeInBytes() {
        return bufferSize.getBytes();
    }

    @Override
    public BlobContainer blobContainer(BlobPath path) {
        return new ObsBlobContainer(path, this);
    }

    @Override
    public void close() throws IOException {
        this.service.close();
    }

    public StorageClassEnum getStorageClass() {
        return storageClass;
    }

    public AccessControlList getAcl() {
        return cannedACL;
    }

    public static StorageClassEnum initStorageClass(String storageClass) {
        if ((storageClass == null) || storageClass.equals("")) {
            return StorageClassEnum.STANDARD;
        }
        try {
            return StorageClassEnum.valueOf(storageClass.toUpperCase(Locale.ENGLISH));
        } catch (final IllegalArgumentException illegalArgumentException) {
            throw new BlobStoreException("`" + storageClass + "` is not a valid obs Storage Class.");
        }
    }

    /**
     * Constructs canned acl from string
     */
    public AccessControlList initAcl(String acl) {
        if ((acl == null) || acl.equals("")) {
            return AccessControlList.REST_CANNED_PRIVATE;
        }
        AccessControlList accessControlList = AclType.getAcLByCode(acl);
        if (accessControlList != null) {
            return accessControlList;
        }
        throw new BlobStoreException("cannedACL is not valid: [" + acl + "]");
    }

    static class Stats {

        final AtomicLong listCount = new AtomicLong();

        final AtomicLong getCount = new AtomicLong();

        final AtomicLong putCount = new AtomicLong();

        final AtomicLong postCount = new AtomicLong();

        Map<String, Long> toMap() {
            final Map<String, Long> results = new HashMap<>();
            results.put("GET", getCount.get());
            results.put("LIST", listCount.get());
            results.put("PUT", putCount.get());
            results.put("POST", postCount.get());
            return results;
        }
    }
}
